/*
 * mana (compiler)
 * Copyright (c) 2003 Shun Moriya <shun@mnu.sakura.ne.jp>
 *
 * The MIT License
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
 * THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

/*!
 * @file
 * @brief	データセクションに関するソースファイル
 *
 * このファイルはデータセクションに関係するソースファイルです。
 *
 * @author	Shun Moriya <shun@mnu.sakura.ne.jp>
 */

#if !defined(___MANA_INFO_H___)
#include "../library/mana_info.h"
#endif
#if !defined(___MANA_MALLOC_H___)
#include "../library/mana_malloc.h"
#endif
#if !defined(___MANA_DATA_H___)
#include "mana_data.h"
#endif
#include <stdio.h>
#include <string.h>

typedef struct mana_data_list
{
	unsigned int address;							/*!< @brief address */
	struct mana_data_list* next;					/*!< @brief next mana_data_list block address */
} mana_data_list;

typedef struct mana_data
{
	mana_data_list* constant;						/*!< @brief Constant list top address */
	char* buffer;									/*!< @brief Constant pool data address */
	unsigned int used_size;							/*!< @brief Constant pool size */
	unsigned int allocated_size;					/*!< @brief Allocated size */
} mana_data;

static mana_data mana_data_buffer;

/*!
 */
void mana_data_initialzie(void)
{
	mana_data_buffer.constant = NULL;
	mana_data_buffer.buffer = NULL;
	mana_data_buffer.used_size = 0;
	mana_data_buffer.allocated_size = 0;
}

/*!
 */
void mana_data_finalize(void)
{
	mana_data_list* list;
	list = mana_data_buffer.constant;
	while(list)
	{
		mana_data_list* next;
		next = list->next;
		mana_free(list);
		list = next;
	}
	mana_data_buffer.constant = NULL;
	if(mana_data_buffer.buffer)
	{
		mana_free(mana_data_buffer.buffer);
		mana_data_buffer.buffer = NULL;
	}
	mana_data_buffer.allocated_size = 0;
	mana_data_buffer.used_size = 0;
}

/*!
 * @return	データセクションバッファのアドレス
 */
char* mana_data_get_buffer(void)
{
	return mana_data_buffer.buffer;
}

/*!
 * @return	データセクションのサイズ
 */
unsigned int mana_data_get_size(void)
{
	return mana_data_buffer.used_size;
}

static mana_data_list* mana_data_find(char* text)
{
	if(text)
	{
		mana_data_list* list;
		list = mana_data_buffer.constant;
		while(list)
		{
			if(strcmp((const char*)&mana_data_buffer.buffer[list->address], (const char*)text) == 0)
				return list;
			list = list->next;
		}
	}
	return NULL;
}

/*!
 * @param[in]	text	テキスト
 * @return		オフセットアドレス
 */
unsigned int mana_data_get(char* text)
{
	mana_data_list* list;
	list = mana_data_find(text);
	return list == NULL ? (unsigned int)-1 : list->address;
}

/*!
 * @param[in]	text	テキスト
 * @return		オフセットアドレス
 */
unsigned int mana_data_set(char* text)
{
	mana_data_list* list;
	list = mana_data_find(text);
	if(list == NULL)
	{
		int length;
		list = (mana_data_list*)mana_calloc(1, sizeof(mana_data_list));
		if(list == NULL)
		{
			mana_data_finalize();
			/* error message */
			return -1;
		}
		length = strlen(text) + 1;
		if(mana_data_buffer.used_size + length >= mana_data_buffer.allocated_size)
		{
			mana_data_buffer.allocated_size += (mana_data_buffer.used_size + length + 4096);
			mana_data_buffer.buffer = (char*)mana_realloc(mana_data_buffer.buffer, mana_data_buffer.allocated_size);
			if(mana_data_buffer.buffer == NULL)
			{
				mana_data_finalize();
				/* error message */
				return -1;
			}
		}
		memcpy(mana_data_buffer.buffer + mana_data_buffer.used_size, text, length);
		list->address = mana_data_buffer.used_size;
		list->next = mana_data_buffer.constant;
		mana_data_buffer.constant = list;
		mana_data_buffer.used_size += length;
	}
	return list->address;
}

/*!
 * @param[out]	file		ファイル識別子
 * @retval		MANA_TRUE	出力成功
 * @retval		MANA_FALSE	出力失敗
 */
unsigned int mana_data_write(FILE* file)
{
	return (fwrite(mana_data_buffer.buffer, sizeof(char), mana_data_buffer.used_size, file) == (unsigned int)mana_data_buffer.used_size);
}

/*!
 * @param[out]	file		ファイル識別子
 * @param[out]	seed		乱数の種
 * @retval		MANA_TRUE	出力成功
 * @retval		MANA_FALSE	出力失敗
 */
unsigned int mana_data_write_encription(FILE* file, unsigned int seed)
{
	unsigned char* buffer;

	buffer = (unsigned char*)mana_malloc(mana_data_buffer.used_size);
	if(buffer)
	{
		unsigned char* pd = buffer;
		unsigned char* ps = (unsigned char*)mana_data_buffer.buffer;
		int result;
		int i;

		mana_srand(seed);
		for(i = 0; i < mana_data_buffer.used_size; i++)
		{
			*pd = *ps ^ (unsigned char)mana_rand();
			pd++;
			ps++;
		}
		result = (fwrite(buffer, sizeof(char), mana_data_buffer.used_size, file) == (unsigned int)mana_data_buffer.used_size);
		mana_free(buffer);
		return result;
	}
	else
	{
		return 0;
	}
}
